package com.example.configuration.core;

import java.util.LinkedList;
import java.util.List;

public abstract class BaseConfiguration<K> implements Configuration<K> {

	private String separator = ",";
	private boolean isThrowException = false;
	
	public static final Converter<Byte> BYTE_CONVERTER = new Converter<Byte>() {
		public Byte convert(String rawValue) {
			return Byte.valueOf(rawValue);
		}
	};
	public static final Converter<Short> SHORT_CONVERTER = new Converter<Short>() {
		public Short convert(String rawValue) {
			return Short.valueOf(rawValue);
		}
	};
	public static final Converter<Integer> INTEGER_CONVERTER = new Converter<Integer>() {
		public Integer convert(String rawValue) {
			return Integer.valueOf(rawValue);
		}
	};
	public static final Converter<Long> LONG_CONVERTER = new Converter<Long>() {
		public Long convert(String rawValue) {
			return Long.valueOf(rawValue);
		}
	};
	public static final Converter<Float> FLOAT_CONVERTER = new Converter<Float>() {
		public Float convert(String rawValue) {
			return Float.valueOf(rawValue);
		}
	};
	public static final Converter<Double> DOUBLE_CONVERTER = new Converter<Double>() {
		public Double convert(String rawValue) {
			return Double.valueOf(rawValue);
		}
	};
	public static final Converter<String> STRING_CONVERTER = new Converter<String>() {
		public String convert(String rawValue) {
			return rawValue;
		}
	};
	public static final Converter<Boolean> BOOLEAN_CONVERTER = new Converter<Boolean>() {
		public Boolean convert(String rawValue) {
			return Boolean.valueOf(rawValue);
		}
	};
	
	public void setSeparator(String separator) {
		this.separator = separator;
	}

	public boolean isThrowException() {
		return isThrowException;
	}

	public void setThrowException(boolean isThrowException) {
		this.isThrowException = isThrowException;
	}

	public <V> V get(Converter<V> c, K key) {
		Pair<K> pair = getPair(key);
		String rawValue = pair.getValue();
		V value = c.convert(rawValue);
		return value;
	}
	
	public <V> V get(Converter<V> c, 
			K key, 
			V defaultValue) {
		
		Pair<K> pair = getPair(key);
		try {
			String rawValue = pair.getValue();
			V value = c.convert(rawValue);
			return value;
		} catch (PairNotFoundException e) {
			if (isThrowException) {
				throw e;
			}
			else {
				return defaultValue;
			}
		}
	}
	
	public <V> V get(Converter<V> c, 
			K key, 
			V defaultValue,
			Constraint<V> constraint) {

		Pair<K> pair = getPair(key);
		try {
			String rawValue = pair.getValue();
			V value = c.convert(rawValue);
			if (constraint.check(value)) {
				return value;
			}
			else {
				if (isThrowException) {
					throw new InvalidValueException(rawValue);
				}
				else {
					return defaultValue;
				}
			}
		} catch (PairNotFoundException e) {
			if (isThrowException) {
				throw e;
			}
			else {
				return defaultValue;
			}
		}
	}

	public <T> List<T> getListOf(Converter<T> c, K key) {
		Pair<K> pair = getPair(key);
		String rawValue = pair.getValue();
		String[] values = rawValue.split(separator);
		List<T> list = new LinkedList<T>();
		for (String value : values) {
			list.add(c.convert(value));
		}
		return list;
	}

	public <T> List<T> getListOf(Converter<T> c, K key, List<T> defaultValues) {
		Pair<K> pair = getPair(key);
		try {
			String rawValue = pair.getValue();
			String[] values = rawValue.split(separator);
			List<T> list = new LinkedList<T>();
			for (String value : values) {
				list.add(c.convert(value));
			}
			return list;
		} catch (PairNotFoundException e) {
			if (isThrowException) {
				throw e;
			}
			else {
				return defaultValues;
			}
		}
	}
	
	public <T> List<T> getListOf(Converter<T> c, 
			K key, 
			List<T> defaultValues,
			Constraint<T> constraint) {
		Pair<K> pair = getPair(key);
		try {
			String rawValue = pair.getValue();
			String[] rawValueArray = rawValue.split(separator);
			List<T> list = new LinkedList<T>();
			for (String rawValueItem : rawValueArray) {
				T value = c.convert(rawValueItem);
				if (constraint.check(value)) {
					list.add(value);
				}
				else {
					if (isThrowException) {
						throw new InvalidValueException(rawValueItem);
					}
					else {
						return defaultValues;
					} 
				}
			}
			
			return list;
		} catch (PairNotFoundException e) {
			if (isThrowException) {
				throw e;
			}
			else {
				return defaultValues;
			}
		}
	}
	
	public byte getByte(K key) {
		return get(BYTE_CONVERTER, key);
	}

	public byte getByte(K key, byte defaultValue) {
		return get(BYTE_CONVERTER, key, defaultValue);
	}
	
	public byte getByte(K key, byte defaultValue, Constraint<Byte> constraint) {
		return get(BYTE_CONVERTER, key, defaultValue, constraint);
	}

	public short getShort(K key) {
		return get(SHORT_CONVERTER, key);
	}
	
	public short getShort(K key, short defaultValue) {
		return get(SHORT_CONVERTER, key, defaultValue);
	}
	
	public short getShort(K key, short defaultValue, Constraint<Short> constraint) {
		return get(SHORT_CONVERTER, key, defaultValue, constraint);
	}

	public int getInteger(K key) {
		return get(INTEGER_CONVERTER, key);
	}
	
	public int getInteger(K key, int defaultValue) {
		return get(INTEGER_CONVERTER, key, defaultValue);
	}
	
	public int getInteger(K key, int defaultValue, Constraint<Integer> constraint) {
		return get(INTEGER_CONVERTER, key, defaultValue, constraint);
	}

	public long getLong(K key) {
		return get(LONG_CONVERTER, key);
	}
	
	public long getLong(K key, long defaultValue) {
		return get(LONG_CONVERTER, key, defaultValue);
	}
	
	public long getLong(K key, long defaultValue, Constraint<Long> constraint) {
		return get(LONG_CONVERTER, key, defaultValue, constraint);
	}

	public float getFloat(K key) {
		return get(FLOAT_CONVERTER, key);
	}
	
	public float getFloat(K key, float defaultValue) {
		return get(FLOAT_CONVERTER, key, defaultValue);
	}
	
	public float getFloat(K key, float defaultValue, Constraint<Float> constraint) {
		return get(FLOAT_CONVERTER, key, defaultValue, constraint);
	}

	public double getDouble(K key) {
		return get(DOUBLE_CONVERTER, key);
	}
	
	public double getDouble(K key, double defaultValue) {
		return get(DOUBLE_CONVERTER, key, defaultValue);
	}
	
	public double getDouble(K key, double defaultValue, Constraint<Double> constraint) {
		return get(DOUBLE_CONVERTER, key, defaultValue, constraint);
	}

	public String getString(K key) {
		return get(STRING_CONVERTER, key);
	}
	
	public String getString(K key, String defaultValue) {
		return get(STRING_CONVERTER, key, defaultValue);
	}
	
	public String getString(K key, String defaultValue, Constraint<String> constraint) {
		return get(STRING_CONVERTER, key, defaultValue, constraint);
	}

	public boolean getBoolean(K key) {
		return get(BOOLEAN_CONVERTER, key);
	}
	
	public boolean getBoolean(K key, boolean defaultValue) {
		return get(BOOLEAN_CONVERTER, key, defaultValue);
	}

	public List<Byte> getListOfByte(K key) {
		return getListOf(BYTE_CONVERTER, key);
	}

	public List<Byte> getListOfByte(K key, List<Byte> defaultValues) {
		return getListOf(BYTE_CONVERTER, key, defaultValues);
	}
	
	public List<Byte> getListOfByte(K key, List<Byte> defaultValues, Constraint<Byte> constraint) {
		return getListOf(BYTE_CONVERTER, key, defaultValues, constraint);
	}

	public List<Short> getListOfShort(K key) {
		return getListOf(SHORT_CONVERTER, key);
	}

	public List<Short> getListOfShort(K key, List<Short> defaultValues) {
		return getListOf(SHORT_CONVERTER, key, defaultValues);
	}
	
	public List<Short> getListOfShort(K key, List<Short> defaultValues, Constraint<Short> constraint) {
		return getListOf(SHORT_CONVERTER, key, defaultValues, constraint);
	}

	public List<Integer> getListOfInteger(K key) {
		return getListOf(INTEGER_CONVERTER, key);
	}
	
	public List<Integer> getListOfInteger(K key, List<Integer> defaultValues) {
		return getListOf(INTEGER_CONVERTER, key, defaultValues);
	}
	
	public List<Integer> getListOfInteger(K key, List<Integer> defaultValues, Constraint<Integer> constraint) {
		return getListOf(INTEGER_CONVERTER, key, defaultValues, constraint);
	}

	public List<Long> getListOfLong(K key) {
		return getListOf(LONG_CONVERTER, key);
	}

	public List<Long> getListOfLong(K key, List<Long> defaultValues) {
		return getListOf(LONG_CONVERTER, key, defaultValues);
	}

	public List<Long> getListOfLong(K key, List<Long> defaultValues, Constraint<Long> constraint) {
		return getListOf(LONG_CONVERTER, key, defaultValues, constraint);
	}

	public List<Float> getListOfFloat(K key) {
		return getListOf(FLOAT_CONVERTER, key);
	}

	public List<Float> getListOfFloat(K key, List<Float> defaultValues) {
		return getListOf(FLOAT_CONVERTER, key, defaultValues);
	}

	public List<Float> getListOfFloat(K key, List<Float> defaultValues, Constraint<Float> constraint) {
		return getListOf(FLOAT_CONVERTER, key, defaultValues, constraint);
	}

	public List<Double> getListOfDouble(K key) {
		return getListOf(DOUBLE_CONVERTER, key);
	}

	public List<Double> getListOfDouble(K key, List<Double> defaultValues) {
		return getListOf(DOUBLE_CONVERTER, key, defaultValues);
	}

	public List<Double> getListOfDouble(K key, List<Double> defaultValues, Constraint<Double> constraint) {
		return getListOf(DOUBLE_CONVERTER, key, defaultValues, constraint);
	}

	public List<String> getListOfString(K key) {
		return getListOf(STRING_CONVERTER, key);
	}
	
	public List<String> getListOfString(K key, List<String> defaultValues) {
		return getListOf(STRING_CONVERTER, key, defaultValues);
	}

	public List<String> getListOfString(K key, List<String> defaultValues, Constraint<String> constraint) {
		return getListOf(STRING_CONVERTER, key, defaultValues, constraint);
	}

	public List<Boolean> getListOfBoolean(K key) {
		return getListOf(BOOLEAN_CONVERTER, key);
	}

	public List<Boolean> getListOfBoolean(K key, List<Boolean> defaultValues) {
		return getListOf(BOOLEAN_CONVERTER, key, defaultValues);
	}
	
}
